/*
This is free software; you can redistribute it and/or modify it
under the terms of the GNU Lesser General Public License as
published by the Free Software Foundation; either version 2.1 of
the License, or (at your option) any later version.

This software is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this software; if not, write to the Free
Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
02110-1301 USA, or see the FSF site: http://www.fsf.org.
 
 */

using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Web.UI;
using System.Web.UI.WebControls;
using Org.Eurekaa.DNJ.Config;
using System.Xml.XPath;
using System.Xml.Xsl;
using System.Xml;
using Org.Eurekaa.DNJ.Helpers.LitJson;

namespace Org.Eurekaa.DNJ.RPC
{
    public static class RPCProcess
    {
        private const string TPL_HEADER = "DNJTPL::HEADER";
        private const string TPL_LOOP = "DNJTPL::LOOP";
        private const string TPL_FOOTER = "DNJTPL::FOOTER";
        private const string TPL_MAPPING_ERROR = "DNJTPL::MAPPING_ERROR";
        private const string TPL_FUNC = "DNJUndefinedSub";
        private const string TPL_PAK = "DNJUndefinedPackage";        
        private const string TPL_MAPPING_FILE = "DNJMAPPING";
        private const string TPL_INVOKER = "DNJUndefinedInvoker";

        private const string TPL_RPC_FACTORY  = "$.DNJRPC.RPCFactory('DNJUndefinedSub', 'DNJUndefinedPackage');";

        private const string JSONRPC_METHOD = "method";
        private const string JSONRPC_PARAMS = "params[]";
        private const string JSONRPC_ID = "id";


        //Deprecated ?
        private static string GetScriptBlock(string scriptTpl, string blockName)
        {
            return scriptTpl.Substring(
                scriptTpl.IndexOf("/*[" + blockName + "]*/") + blockName.Length + 6,
                scriptTpl.IndexOf("/*[/" + blockName + "]*/") - scriptTpl.IndexOf("/*[" + blockName + "]*/") - blockName.Length - 7);
        }



        public static string ScriptGenerator(HttpContext context)
        {
            StringBuilder sbOut = new StringBuilder();


            DNJConfiguration dnjConfig = ConfigReader.GetConfiguration(context.Server.MapPath(context.Request.QueryString["conf"]));
            if (dnjConfig == null)
            {
                sbOut.Append("/*Configuration file not found : " + context.Request.QueryString["conf"] + "*/");
                return sbOut.ToString();
            }
            Assembly assembly = Assembly.GetExecutingAssembly();                
            //string[] names = assembly.GetManifestResourceNames();            
            Stream stream = assembly.GetManifestResourceStream("Org.Eurekaa.DNJ.Resources.jquery.dnjrpc.js");
            StreamReader streamReader = new StreamReader(stream);
            String scriptTpl = streamReader.ReadToEnd();


            //string headerTpl = GetScriptBlock(scriptTpl, TPL_HEADER);
            //string loopTpl = GetScriptBlock(scriptTpl, TPL_LOOP);
            //string footerTpl = GetScriptBlock(scriptTpl, TPL_FOOTER);
            string invoker = context.Request.CurrentExecutionFilePath + "?conf=" + context.Request.QueryString["conf"];


            

            //Generate the script            
            sbOut.Append(scriptTpl.Replace(TPL_INVOKER, invoker));
            foreach (DNJConfigFunction f in dnjConfig.Functions)
            {
                sbOut.Append(TPL_RPC_FACTORY.Replace(TPL_FUNC, f.Name).Replace(TPL_INVOKER, invoker).Replace(TPL_PAK, f.Assembly));
            }
            //sbOut.Append(footerTpl);


            return sbOut.ToString();
        }
        public static void InvokerProcess(HttpContext context)
        {
            //Set current culture to en-US, this is useful to because javascript numbers notation is the same as 
            //en-US contexts (ie: 1.2345 for float and double types)
            System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo("en-US");

            JSONRPCResponse jsonResp = new JSONRPCResponse(context.Request.Form[JSONRPC_ID]);
            
            //Read Json configuration first
            DNJConfiguration dnjConfig = ConfigReader.GetConfiguration(context.Server.MapPath(context.Request.QueryString["conf"]));
            if (dnjConfig == null)
            {
                jsonResp.error = "/*DNJ Exception, Configuration file not found : " + context.Request.QueryString["conf"] + "*/";
            }



            foreach (DNJConfigFunction f in dnjConfig.Functions)
            {
                //check called function against mapping
                if (f.Name != context.Request.Form[JSONRPC_METHOD]) continue;


                try
                {
                    //loading assembly and type 
                    Assembly asmm = Assembly.Load(f.Assembly);
                    Type TypeToReflect = asmm.GetType(f.Type);

                    //non static methods need instance
                    jsonResp.result = TypeToReflect.GetConstructor(new Type[0]).Invoke(null);


                    string[] jsonparams = context.Request.Form.GetValues(JSONRPC_PARAMS);
                    object[] arguments = jsonparams != null ? new object[jsonparams.Length] : null;

                    //The arguments was loaded but are string typed, we need to convert them
                    if (jsonparams != null && jsonparams.Length > 0)
                    {                                        
                        MethodInfo[] methods = TypeToReflect.GetMethods(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance);                        
                        foreach (MethodInfo m in methods)
                        {
                            if (m.Name != context.Request.Form[JSONRPC_METHOD]) continue;
                            ParameterInfo[] pars = m.GetParameters();
                            if (pars == null || pars.Length != arguments.Length) continue;
                            jsonResp.error = null;                                
                            try
                            {                                
                                for (int i = 0; i < pars.Length; i++)
                                {
                                    arguments[i] = Convert.ChangeType(jsonparams[i], pars[i].ParameterType);
                                }
                            }
                            catch { jsonResp.error = "Argument format error"; continue; }
                        }
                    }
                    if (jsonResp.error != null) throw new Exception(jsonResp.error);
                    //Allright we can invoke the method now !                                      
                    jsonResp.result = TypeToReflect.InvokeMember(context.Request.Form[JSONRPC_METHOD], BindingFlags.InvokeMethod, null, jsonResp.result, arguments);
                }
                catch (Exception ex)
                {
                    //Ouups something goes wrong : we return the error message
                    jsonResp.result = null;
                    jsonResp.error = ex.Message;
                }
                break;


            }


            context.Response.Write(JsonMapper.ToJson(jsonResp));
            

        }



    }
}
